package com.jddup.program.dao.handler.impl;

import com.jddup.common.exception.JddupException;
import com.jddup.common.pojo.TableColumn;
import com.jddup.common.pojo.TableMeta;
import com.jddup.program.dao.handler.IQueryHandler;
import com.jddup.program.dao.sqlbuilder.QueryPrepareSQLBuilder;
import com.jddup.program.etm.EntityToTableMetaConvertor;
import com.jddup.program.utils.POJOUtils;

import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by ZZB on 2017/10/22.
 */
public class QueryHandlerImpl implements IQueryHandler {
	private QueryPrepareSQLBuilder queryPrepareSQLBuilder;
	private EntityToTableMetaConvertor entityToTableMetaConvertor;

	public QueryHandlerImpl(QueryPrepareSQLBuilder queryPrepareSQLBuilder, EntityToTableMetaConvertor entityToTableMetaConvertor) {
		this.queryPrepareSQLBuilder = queryPrepareSQLBuilder;
		this.entityToTableMetaConvertor = entityToTableMetaConvertor;
	}

	@Override
	public <T> T getBy(Object pk, Class<T> tClass, Connection connection) {
		TableMeta tableMeta = entityToTableMetaConvertor.convertFromClass(tClass);
		return findBy(tableMeta.getPrimaryKeyColumn().getColumnName(), pk, tClass, connection);
	}

	@Override
	public <T> T findBy(final String c, Object o, Class<T> tClass, Connection connection) {
		List<T> list = queryBy(c, o, tClass, connection);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	@Override
	public <T> List<T> queryBy(final String c, Object o, Class<T> tClass, Connection connection) {
		TableColumn tableColumn = entityToTableMetaConvertor.obtainByFieldName(tClass, c);
		String columnName;
		if (tableColumn != null) {
			columnName = tableColumn.getColumnName();
			TableMeta tableMeta = entityToTableMetaConvertor.convertFromClass(tClass);
			String sql = queryPrepareSQLBuilder.queryByFieldSQL(tableMeta.getTableName(), columnName);
			return query(sql, tClass, new Object[] { o }, connection);
		}
		return null;
	}

	@Override
	public <T> List<T> query(String sql, Class<T> tClass, Connection connection) {
		try {
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);
			return readFromResultSet(resultSet, tClass);
		} catch (Exception e) {
			throw new JddupException(e);
		}
	}

	@Override
	public <T> List<T> query(String sql, Class<T> tClass, Object[] objects, Connection connection) {
		try {
			PreparedStatement preparedStatement = connection.prepareStatement(sql);
			for (int i = 0; i < objects.length; i++) {
				preparedStatement.setObject(i + 1, objects[i]);
			}
			ResultSet resultSet = preparedStatement.executeQuery();
			return readFromResultSet(resultSet, tClass);
		} catch (Exception e) {
			throw new JddupException(e);
		}
	}

	private <T> List<T> readFromResultSet(ResultSet resultSet, Class<T> tClass)
			throws SQLException, IllegalAccessException, InstantiationException, NoSuchMethodException,
			NoSuchFieldException, InvocationTargetException {
		TableMeta tableMeta = entityToTableMetaConvertor.convertFromClass(tClass);
		List<T> list = new ArrayList<>();
		while (resultSet.next()) {
			T t = tClass.newInstance();
			List<TableColumn> tableColumns = tableMeta.getTableColumns();
			for (TableColumn tableColumn : tableColumns) {
				String columnName = tableColumn.getColumnName();
				POJOUtils.writeDeclaredFieldBySetMethod(t, tableColumn.getPropertyName(),
						resultSet.getObject(columnName));
			}
			list.add(t);
		}
		return list;
	}

	private long countFromResultSet(ResultSet resultSet) throws SQLException {
		if (resultSet.next()) {
			return resultSet.getLong(1);
		}
		return 0L;
	}

	@Override
	public long queryCount(String sql, Connection connection) {
		try {
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);
			return countFromResultSet(resultSet);
		} catch (Exception e) {
			throw new JddupException(e);
		}
	}

	@Override
	public long queryCount(String sql, Object[] objects, Connection connection) {
		try {
			PreparedStatement preparedStatement = connection.prepareStatement(sql);
			for (int i = 0; i < objects.length; i++) {
				preparedStatement.setObject(i + 1, objects[i]);
			}
			ResultSet resultSet = preparedStatement.executeQuery();
			return countFromResultSet(resultSet);
		} catch (Exception e) {
			throw new JddupException(e);
		}
	}

	@Override
	public <T> T getBy(Object pk, Class<T> tClass, String tableName, Connection connection) {
		TableMeta tableMeta = entityToTableMetaConvertor.convertFromClass(tClass);
		tableMeta.setTableName(tableName);
		return findBy(tableMeta.getPrimaryKeyColumn().getColumnName(), pk, tClass, connection);
	}

	@Override
	public <T> T findBy(String c, Object o, Class<T> tClass, String tableName, Connection connection) {
		List<T> list = queryBy(c, o, tClass, tableName, connection);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	@Override
	public <T> List<T> queryBy(String c, Object o, Class<T> tClass, String tableName, Connection connection) {
		TableColumn tableColumn = entityToTableMetaConvertor.obtainByFieldName(tClass, c);
		String columnName;
		if (tableColumn != null) {
			columnName = tableColumn.getColumnName();
			String sql = queryPrepareSQLBuilder.queryByFieldSQL(tableName, columnName);
			return query(sql, tClass, new Object[] { o }, connection);
		}
		return null;
	}

	@Override
	public <T> long countBy(String c, Object o, Class<T> tClass, String tableName, Connection connection) {
		String sql = queryPrepareSQLBuilder.countByFieldSQL(tableName, c);
		return queryCount(sql, new Object[] { o }, connection);
	}

	@Override
	public <T> long countBy(String c, Object o, Class<T> tClass, Connection connection) {
		TableColumn tableColumn = entityToTableMetaConvertor.obtainByFieldName(tClass, c);
		if (tableColumn != null) {
			c = tableColumn.getColumnName();
		}

		TableMeta tableMeta = entityToTableMetaConvertor.convertFromClass(tClass);
		String sql = queryPrepareSQLBuilder.countByFieldSQL(tableMeta.getTableName(), c);
		return queryCount(sql, new Object[] { o }, connection);
	}
}
